<?php

// **************************************************************
// | Name: 星空框架 OSS存储服务
// **************************************************************
// | Version: V5.0
// **************************************************************
// | Author: xkyinzhe <zhonghuaxinxing@sina.cn>
// **************************************************************
// | Time: 2021-07-09 16:12
// **************************************************************
declare(strict_types=1); // 强制严格模式

namespace app\common\services;

use OSS\Core\OssException; // 引入OSS 链接
use OSS\OssClient; // 引入OSS异常处理

/**
 * @xk OSS存储服务
 */
class OSS
{
    const isSSl = true;
    /**
     * @xk 入口
     *
     * @method __construct
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T16:17:09+080
     */
    public function __construct()
    {
        $this->config = config('oss');
        self::createClient();
    }
    // 初始化定义链接
    private static $ossClient;
    /**
     * @xk 创建连接实例
     *
     * @method createClient
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T16:24:52+080
     */
    private function createClient()
    {
        try {
            self::$ossClient = new OssClient(
                $this->config['AccessKeyId'],
                $this->config['AccessKeySecret'],
                $this->config['EndPoint']
            );
            // 设置Socket层传输数据的超时时间，单位秒，默认5184000秒。
            self::$ossClient->setTimeout(3600);
            // 设置建立连接的超时时间，单位秒，默认10秒。
            self::$ossClient->setConnectTimeout(10);
        } catch (OssException $e) {
            $resultData = [];
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();

            return $resultData;
        }
    }

    // ****************
    // | Bucket相关
    // *******************

    /**
     * @xk 检测存储空间是否存在
     *
     * @method checkBucket
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T16:45:00+080
     */
    public function checkBucket()
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '存储空间存在，继续执行上传';
        try {
            $res = self::$ossClient->doesBucketExist($this->config['Bucket']);
            if (!$res) {
                $resultData = self::createBucket(); // 如果存储空间不存在 执行存储空间创建
            }
        } catch (OssException $e) {
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();
        }

        return $resultData;
    }

    /**
     * @xk 创建存储空间
     *
     * @method createBucket
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T16:46:25+080
     */
    public function createBucket()
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '存储空间可用';
        try {
            // 设置存储空间的存储类型为低频访问类型，默认是标准类型。
            $options = [
          OssClient::OSS_STORAGE => OssClient::OSS_STORAGE,
      ];
            $res = self::$ossClient->createBucket($this->config['Bucket'], OssClient::OSS_ACL_TYPE_PUBLIC_READ, $options);
        } catch (OssException $e) {
            $resultData['result_code'] = 40004;
            $resultData['result_msg'] = $e->getMessage();
        }

        return $resultData;
    }

    // *****************
    // | 简单上传
    // ******************

    /**
     * @xk 字符串上传
     *
     * @method stringUpload
     *
     * @param [type] $ossFileName [字符串名称]
     * @param [type] $content     [字符串内容]
     * @param string $options     [oss自定义header]
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T17:12:27+080
     */
    public function stringUpload($ossFileName, $content, $options = '')
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '上传成功';
        $resultData['resultData'] = [];
        try {
            // 上传时可以设置相关的headers，例如设置访问权限为private和自定义元信息。
            // $options = array(
            //     OssClient::OSS_HEADERS => array(
            //         'x-oss-object-acl' => 'private',
            //         'x-oss-meta-info' => 'your info'
            //     ),
            // );
            $resultData['resultData'] = self::$ossClient->putObject($this->config['Bucket'], $ossFileName, $content, $options);
        } catch (OssException $e) {
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();
        }

        return $resultData;
    }

    /**
     * @xk 文件上传
     *
     * @method fileUpload
     *
     * @param [type] $ossFileName [文件名称]
     * @param [type] $uploadFile  [文件本地路径]
     * @param string $options     [oss头部自定义参数]
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T17:11:40+080
     */
    public function fileUpload($ossFileName, $uploadFile, $options = '')
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '上传成功';
        $resultData['resultData'] = [];
        try {
            // 上传时可以设置相关的headers，例如设置访问权限为private和自定义元信息。
            // $options = array(
            //     OssClient::OSS_HEADERS => array(
            //         'x-oss-object-acl' => 'private',
            //         'x-oss-meta-info' => 'your info'
            //     ),
            // );
            $resultData['resultData'] = self::$ossClient->uploadFile($this->config['Bucket'], $ossFileName, $uploadFile, $options);
        } catch (OssException $e) {
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();
        }

        return $resultData;
    }

    /**
     * @xk 字符串追加上传
     *
     * @method stringAppendUpload
     *
     * @param [type] $ossFileName [description]
     * @param [type] $contentArr  [description]
     * @param string $options     [description]
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T17:21:01+080
     */
    public function stringAppendUpload($ossFileName, $contentArr)
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '上传成功';
        $resultData['resultData'] = [];
        try {
            if (!in_array($contentArr)) {
                throw new OssException('上传数据格式需要是数组', 4004);
            }
            $position = 0;
            foreach ($contentArr as $key => $value) {
                if (!is_string($value)) {
                    continue;
                }
                $position = self::$ossClient->appendObject($this->config['Bucket'], $ossFileName, $value, $position);
            }
        } catch (OssException $e) {
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();
        }

        return $resultData;
    }

    /**
     * @xk 文件追加上传
     *
     * @method fileAppendUpload
     *
     * @param [type] $ossFileName   [description]
     * @param [type] $uploadFileArr [description]
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T17:29:20+080
     */
    public function fileAppendUpload($ossFileName, $uploadFileArr)
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '上传成功';
        $resultData['resultData'] = [];
        try {
            if (!in_array($contentArr)) {
                throw new OssException('上传数据格式需要是数组', 4004);
            }
            $position = 0;
            foreach ($uploadFileArr as $key => $value) {
                if (!is_string($value)) {
                    continue;
                }
                $position = self::$ossClient->appendFile($this->config['Bucket'], $ossFileName, $value, $position);
            }
        } catch (OssException $e) {
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();
        }

        return $resultData;
    }
    /**
     * @xk 文件分片上传
     *
     * @method multiFileUpload
     * @param  [type]                  $ossFileName              [description]
     * @param  [type]                  $uploadFile               [description]
     * @return [type]                                            [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-09T17:45:17+080
     */
    public function multiFileUpload($ossFileName, $uploadFile)
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '上传成功';
        $resultData['resultData'] = [];
        try {
            //返回uploadId。uploadId是分片上传事件的唯一标识，您可以根据uploadId发起相关的操作，如取消分片上传、查询分片上传等。
            $uploadId = self::$ossClient->initiateMultipartUpload($this->config['Bucket'], $ossFileName);
            /*
             * 步骤2：上传分片。
             */
            $partSize = 10 * 1024 * 1024;
            $uploadFileSize = filesize($uploadFile);
            $pieces = self::$ossClient->generateMultiuploadParts($uploadFileSize, $partSize);
            $responseUploadPart = array();
            $uploadPosition = 0;
            $isCheckMd5 = true;
            foreach ($pieces as $i => $piece) {
                $fromPos = $uploadPosition + (integer)$piece[self::$ossClient::OSS_SEEK_TO];
                $toPos = (integer)$piece[self::$ossClient::OSS_LENGTH] + $fromPos - 1;
                $upOptions = array(
                // 上传文件。
                self::$ossClient::OSS_FILE_UPLOAD => $uploadFile,
                // 设置分片号。
                self::$ossClient::OSS_PART_NUM => ($i + 1),
                // 指定分片上传起始位置。
                self::$ossClient::OSS_SEEK_TO => $fromPos,
                // 指定文件长度。
                self::$ossClient::OSS_LENGTH => $toPos - $fromPos + 1,
                // 是否开启MD5校验，true为开启。
                self::$ossClient::OSS_CHECK_MD5 => $isCheckMd5,
            );
                // 开启MD5校验。
                if ($isCheckMd5) {
                    $contentMd5 = OssUtil::getMd5SumForFile($uploadFile, $fromPos, $toPos);
                    $upOptions[self::$ossClient::OSS_CONTENT_MD5] = $contentMd5;
                }
                // 上传分片。
                $responseUploadPart[] = self::$ossClient->uploadPart($this->config['Bucket'], $ossFileName, $uploadId, $upOptions);
            }
            // $uploadParts是由每个分片的ETag和分片号（PartNumber）组成的数组。
            $uploadParts = array();
            foreach ($responseUploadPart as $i => $eTag) {
                $uploadParts[] = array(
                'PartNumber' => ($i + 1),
                'ETag' => $eTag,
            );
            }
            self::$ossClient->completeMultipartUpload($this->config['Bucket'], $ossFileName, $uploadId, $uploadParts);
        } catch (OssException $e) {
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();
        }
        return $resultData;
    }
    /**
     * @xk 获取带签名的访问链接
     *
     * @method getSignShowUrl
     * @param  [type]                  $ossFileName              [description]
     * @return [type]                                            [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-07-14T15:48:15+080
     */
    public function getSignShowUrl($ossFileName)
    {
        try {
            $ossFileName = substr($ossFileName, 1);
            $timeout = 3600 * 24;
            $signedUrl = self::$ossClient->signUrl($this->config['Bucket'], $ossFileName, $timeout, "PUT");
            // $signUrl = self::$ossClient->signUrl($this->config['Bucket'], $ossFileName, $timeout);
            return $signedUrl;
        } catch (OssException $e) {
            return $e->getMessage();
        }
    }
    // +-----------------
    // | 文件删除
    // +---------------------
    /**
     * @xk 删除文件
     *
     * @method destroyUrl
     * @param  [type]     $ossFileName               [description]
     * @return [type]     [description]
     * @Time   2021-08-17
     * @City   qingdao
     * @Author xkyinzhe   <zhonghuaxinxing@sina.cn>
     */
    public function destroyUrl($ossFileName)
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '删除成功';
        $resultData['resultData'] = [];
        try {
            $resultData['resultData'] = self::$ossClient->deleteObject($this->config['Bucket'], $ossFileName);
        } catch (OssException $e) {
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();
        }
        return $resultData;
    }
    /**
     * @xk 多文件删除
     *
     * @method destroyAppendUrl
     * @param  [type]           $ossFileName               [description]
     * @return [type]           [description]
     * @Time   2021-08-17
     * @City   qingdao
     * @Author xkyinzhe         <zhonghuaxinxing@sina.cn>
     */
    public function destroyAppendUrl($ossFileNameArr)
    {
        $resultData = [];
        $resultData['result_code'] = 2000;
        $resultData['result_msg'] = '删除成功';
        $resultData['resultData'] = [];
        try {
            if (!in_array($ossFileNameArr)) {
                throw new OssException('需要删除的数据格式必须是数组', 4004);
            }
            self::$ossClient->deleteObjects($this->config['Bucket'], $ossFileNameArr);
        } catch (OssException $e) {
            $resultData['result_code'] = 4004;
            $resultData['result_msg'] = $e->getMessage();
        }
        return $resultData;
    }
}
